using System;
using System.Collections.Generic;
using System.Diagnostics;
using Apache.Cassandra;
using Cassandraemon.Builder;


namespace Cassandraemon.Operators
{


	class GetCassandraOperator<T> : CassandraOperatorBase<T>
	{
		public byte[] Key { get; set; }
		
		public ColumnPath ColumnPath { get; set; }
		
		public ConsistencyLevel ConsistencyLevel { get; set; }
	
	
		public GetCassandraOperator () { }
		
		
		protected override object OperateInner ()
		{
            var ctx = this.Context;
            var sw = Stopwatch.StartNew();
			ColumnOrSuperColumn column;
			try {
				column = Client.get(Key, ColumnPath, ConsistencyLevel);
			} catch (NotFoundException) {
                ctx.OnExecuted(CassandraExecuteEventArgs.FromOperation(this, null, sw));
				return new List<CassandraEntity<T>>();
            } catch (Exception err) {
                ctx.OnExecuted(CassandraExecuteEventArgs.FromOperation(this, err, sw));
                throw;
            }
            ctx.OnExecuted(CassandraExecuteEventArgs.FromOperation(this, column, sw));

			T t = (T)Activator.CreateInstance(typeof(T));
			
			if(t is Column)
			{
				return new List<CassandraEntity<T>>{
					new CassandraEntity<T>{Context = this.Context,
												Key = this.Key,
												ColumnFamily = this.ColumnPath.Column_family,
												SuperColumn = this.ColumnPath.Super_column,
												Column = this.ColumnPath.Column,
												Data = (T)(object)column.Column
					}
				};
			}
			else if(t is SuperColumn)
			{
				return new List<CassandraEntity<T>>{
					new CassandraEntity<T>{Context = this.Context,
												Key = this.Key,
												ColumnFamily = this.ColumnPath.Column_family,
												SuperColumn = this.ColumnPath.Super_column,
												Column = this.ColumnPath.Column,
												Data = (T)(object)column.Super_column
					}
				};
			}
			else if(t is CounterColumn)
			{
				return new List<CassandraEntity<T>>{
					new CassandraEntity<T>{Context = this.Context,
												Key = this.Key,
												ColumnFamily = this.ColumnPath.Column_family,
												SuperColumn = this.ColumnPath.Super_column,
												Column = this.ColumnPath.Column,
												Data = (T)(object)column.Counter_column
					}
				};
			}
			else if(t is CounterSuperColumn)
			{
				return new List<CassandraEntity<T>>{
					new CassandraEntity<T>{Context = this.Context,
												Key = this.Key,
												ColumnFamily = this.ColumnPath.Column_family,
												SuperColumn = this.ColumnPath.Super_column,
												Column = this.ColumnPath.Column,
												Data = (T)(object)column.Counter_super_column
					}
				};
			}
			else
			{
				throw new InvalidOperationException("You can specify Column or SuperColumn or CounterColumn or CounterSuperColumn to CassandraEntity's GenericType in get operation."); 
			}
		}

#if TRACE
        protected internal override void GetTraceData(List<object> data)
        {
            base.GetTraceData(data);
            data.Add("get");
            var cp = this.ColumnPath;
            data.Add(cp.Column_family);
        }
#endif

		public static ICassandraOperator<T> Create(CassandraOperatorCondition<T> condition)
		{
			if(!condition.IsSetKey) throw new InvalidOperationException("You must specify Key in case of get operation.");
			if(condition.IsSetKeys) throw new InvalidOperationException("You can't specify Keys in case of get operation.");
			if(condition.IsSetColumns) throw new InvalidOperationException("You can't specify Columns in case of get operation.");
			
			if(condition.IsSetStartKey || 
			   condition.IsSetFinishKey || 
			   condition.IsSetStartToken || 
			   condition.IsSetFinishToken ||
			   condition.IsSetKeyCount)
				throw new InvalidOperationException("You can't specify range key in case of get operation.");
	
			if(condition.IsSetStartColumn || 
			   condition.IsSetFinishColumn || 
			   condition.IsSetReverse ||
			   condition.IsSetCount)
				throw new InvalidOperationException("You can't specify range column in case of get operation.");
			
			
			var oper = new GetCassandraOperator<T>();
			
			oper.Key = condition.Key;
			oper.ConsistencyLevel = condition.ConsistencyLevel;
			oper.PostProcessor = condition.PostProcessor;
			
			oper.ColumnPath = new ColumnPath();
			oper.ColumnPath.Column_family = condition.ColumnFamily;
			oper.ColumnPath.Super_column = condition.SuperColumn;
			
			
			Type type = typeof(T);
			
			if(type.Equals(typeof(Column)) || type.Equals(typeof(CounterColumn)))
		   	{
				if(!condition.IsSetColumn) throw new InvalidOperationException("You must specify Column in case of get Column.");
				oper.ColumnPath.Column = condition.Column;
			}
			else if(type.Equals(typeof(SuperColumn)) || type.Equals(typeof(CounterSuperColumn)))
			{
				if(condition.IsSetColumn) throw new InvalidOperationException("You can't specify Column in case of get SuperColumn.");
			}
			else
			{
				throw new InvalidOperationException("You must specify either Column or SuperColumn or CounterColumn or CounterSuperColumn to CassandraEntity's GenericType in get operation."); 
			}
			
			return oper;
		}
	}
}
